﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CamShared;
using System.IO;
using System.Drawing;
using System.Threading;
using System.Diagnostics;
using DavuxLib;

namespace Webserver
{
    public class WebServer :ICameraOutput
    {


        public static List<ICamera> Cameras = new List<ICamera>();
        public static List<Image> Frames = new List<Image>();
        public static List<object> Locks = new List<object>();
        Dictionary<ICamera, int> Timeout = new Dictionary<ICamera, int>();
        Dictionary<ICamera, bool> Acticated = new Dictionary<ICamera, bool>();



        public WebServer()
        {
            Settings.Get("Simple Webserver|Enabled", true);
            Settings.Get("Simple Webserver|UserName", "dave");
            Settings.Get("Simple Webserver|Password", "dave");
            Settings.Get("Simple Webserver|Port", 8008);
            //Settings.SetRestartRequired("Simple Webserver|Port");
            Settings.Get("Simple Webserver|HTTP Realm", "Camera Console");
            Settings.Get("Simple Webserver|Keep-Alive Enabled", true);

            if (Settings.Get("Simple Webserver|Enabled", true))
            {
                HTTPServer s = new HTTPServer(Settings.Get("Simple Webserver|Port", 8008));
                s.OnRequest += new HTTPClient.RequestHandler(s_OnRequest);

                try
                {
                    s.Start();
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("Couldn't start webserver: " + ex.Message);
                }
                Thread t = new Thread(ActiveCheckThread);
                t.Start();
            }
        }


        private void ActiveCheckThread()
        {
            Thread.CurrentThread.Name = "Wweb WatchDog";
            while (true)
            {
                try
                {
                    for (int i = Timeout.Count - 1; i >= 0; i--)
                    {
                        KeyValuePair<ICamera, int> k = Timeout.ElementAt(i);

                        Timeout[k.Key]--;

                        if (Timeout[k.Key] <= 0)
                        {
                            if (Acticated[k.Key])
                            {
                                Trace.WriteLine("Deactivating Camera " + k.Key.Name);
                                Acticated[k.Key] = false;
                                k.Key.Deactivate();
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("Error deactivating camera: " + ex);
                }
                Thread.Sleep(1000);
            }
        }

        void AuthenticatedRequest(HTTPClient client, string url)
        {
            string qs = "";
            Dictionary<string, string> QueryString = new Dictionary<string, string>();

            if (url.Contains('?'))
            {
                qs = url.Substring(url.IndexOf('?'));
                if (qs.Length > 1)
                {
                    qs = qs.Substring(1); // remove ?
                }
                url = url.Substring(0, url.IndexOf('?'));
                QueryString = MakeQueryString(qs);
            }

            if (url.StartsWith("/cam"))
            {
                // cam service
                int camID;
                if (int.TryParse(url.Substring(4), out camID))
                {
                    if (camID >= 0 && camID < Cameras.Count)
                    {
                        ICamera c = Cameras[camID];
                        Timeout[c] = 20;
                        try
                        {
                            if (!c.Active || !Acticated[c])
                            {
                                Acticated[c] = true;
                                c.Activate();
                                Trace.WriteLine("Webserver Activated Camera " + c.Name + " for " + client.Socket.RemoteEndPoint);
                            }
                        }
                        catch (Exception ex)
                        {
                            Trace.WriteLine("Couldn't Activate camera: " + ex.Message);
                        }

                        Image b = Frames[camID];
                        if (b != null)
                        {
                            lock (Locks[camID])
                            {
                                b = (Image)Frames[camID].Clone();
                            }
                            Image result = b;
                            byte[] img = null;

                            if (QueryString.ContainsKey("x") && QueryString.ContainsKey("y"))
                            {
                                try
                                {
                                    result = new Bitmap(int.Parse(QueryString["x"]), int.Parse(QueryString["y"]));
                                    using (Graphics g = Graphics.FromImage(result))
                                    {
                                        g.DrawImage(b, 0, 0, int.Parse(QueryString["x"]), int.Parse(QueryString["y"]));
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Trace.WriteLine("Errorous Request: " + ex);
                                }
                            }

                            long quality = 50;

                            if (QueryString.ContainsKey("q"))
                            {
                                quality = int.Parse(QueryString["q"]);
                            }


                            img = DavuxLib.ImgTools.GetJpeg(result, quality);

                            client.SendResponseHeader("200 OK");
                            client.SendHeader("Content-Type: image/jpeg");
                            client.SendHeader("Content-Length: " + img.Length);
                            client.EndHeader();
                            client.Socket.Send(img);
                        }
                        else
                        {
                            client.SendResponseHeader("200 OK");
                            client.SendHeader("Content-Type: image/jpeg");
                            client.SendHeader("Content-Length: 0");
                            client.EndHeader();
                        }

                        return;
                    }
                }
            }

            if (url == "/settings")
            {
                string list = "";
                foreach (ConfValue c in Settings.Dict)
                {
                    list += c.Key + "||" + c.Value + "\r\n";
                }
                client.SendResponseHeader("200 OK");
                client.SendHeader("Content-Type: text/html");
                client.SendHeader("Content-Length: " + list.Length);
                client.EndHeader();

                StreamWriter sw = new StreamWriter(client.Client.GetStream());
                sw.WriteLine(list);
                sw.Flush();
                return;
            }

            if (url == "/list")
            {
                string list = "";
                foreach (ICamera c in Cameras)
                {
                    list += c.Name + "\n";
                }
                client.SendResponseHeader("200 OK");
                client.SendHeader("Content-Type: text/html");
                client.SendHeader("Content-Length: " + list.Length);
                client.EndHeader();

                StreamWriter sw = new StreamWriter(client.Client.GetStream());
                sw.WriteLine(list);
                sw.Flush();
                return;
            }

            
            url = url.Replace('/', System.IO.Path.DirectorySeparatorChar);
            url = url.Replace('\\', System.IO.Path.DirectorySeparatorChar);
            url = url.Replace("..", "");
            url = url.Replace(":", "");

            if (url.Trim() == System.IO.Path.DirectorySeparatorChar.ToString())
            {
                url = "default.htm";
            }

            //Trace.WriteLine("Request for: " + url);
            if (File.Exists("html" + System.IO.Path.DirectorySeparatorChar + url))
            {
                //Trace.WriteLine("Serving: " + url);
                byte[] f = File.ReadAllBytes("html" + System.IO.Path.DirectorySeparatorChar + url);
                client.SendResponseHeader("200 OK");
                client.SendHeader("Content-Type: " + client.GetMimeTypeFromFile("html" + System.IO.Path.DirectorySeparatorChar + url));
                client.SendHeader("Content-Length: " + f.Length);
                client.EndHeader();

                client.Socket.Send(f);


                return;
            }
            



            client.SendResponseHeader("404 Not Found");
            client.EndHeader();
        }

        private static Dictionary<string, string> MakeQueryString(string qs)
        {
            Dictionary<string, string> q = new Dictionary<string, string>();

            if (qs.Contains('&'))
            {
                // multiple values
                string[] pairs = qs.Split(new char[] { '&' });
                foreach (string s in pairs)
                {
                    KeyValuePair<string, string> kv = GetQueryStringValue(s);
                    q.Add(kv.Key, kv.Value);
                }
            }
            else
            {
                KeyValuePair<string, string> kv = GetQueryStringValue(qs);
                q.Add(kv.Key, kv.Value);
            }
            return q;
        }

        private static KeyValuePair<string, string> GetQueryStringValue(string q)
        {
            string key = "";
            string value = "";
            if (q.IndexOf('=') > -1)
            {
                key = q.Substring(0, q.IndexOf('=')).ToLower();
                value = q.Substring(key.Length);
                if (value.Length > 0)
                {
                    value = value.Substring(1);
                }
            }
            else
            {
                key = q;
                value = "";
            }
            return new KeyValuePair<string, string>(key, value);
        }

        void s_OnRequest(HTTPClient client, string url, string method, string body)
        {
            string a = client.GetHeader("authorization");
            if (Settings.Get("Simple Webserver|Require Authentication", true) &&

                !client.AuthorizationCheck(Settings.Get("Simple Webserver|UserName", "dave"), Settings.Get("Simple Webserver|Password", "dave")))
            {
                client.SendResponseHeader("401 Authorization Required");
                client.SendHeader("WWW-Authenticate: Basic realm=\"" + Settings.Get("Simple Webserver|HTTP Realm", "Camera Console") + "\"");
                client.SendPage("Authentication is Required for this resource.");
                client.EndHeader();
            }
            else
            {
                AuthenticatedRequest(client, url);
            }
        }



        public void FrameReceived(ICamera camera, System.Drawing.Image frame)
        {
            for (int i = 0; i < Cameras.Count; i++)
            {
                if (camera == Cameras[i])
                {
                    lock (Locks[i])
                    {
                        Frames[i] = frame;
                    }
                    return;
                }
            }
        }

        public void ErrorOccured(ICamera camera, string Error, bool Fatal)
        {
            Trace.WriteLine("WS ErrorOccursed: " + camera.Name + " " + Error);
        }

        public void CameraAttached(ICamera camera)
        {
            Cameras.Add(camera);
            Frames.Add(null);
            Locks.Add(new object());
        }

        public string Name
        {
            get { return "Simple Webserver"; }
        }


        public void Dispose()
        {
            throw new NotImplementedException();
        }


    }
}
